<template>
  <div>
    <div class="wrap" :class="{'disabled': disabled}">
      <div ref="slider" class="slider"></div>
      <div class="main flex">
        <div class="content" v-for="(item, index) in options"
             :key="item.value" @click="change(item.value, index)"
             :class="{'active': item.value === checked}">
          {{ item.label }}
        </div>
      </div>
    </div>
  </div>
</template>

<script>
/**
 * Magic. Do not touch.
 * Author：sirio，2020/03/09
 */
export default {
  name: 'CustomizedSwitch',
  props: {
    options: Array,
    value: {
      type: [Number, String]
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    checked: {
      get: function () {
        return this.newVal
      },
      set: function (val) {
        this.newVal = val
      }
    }
  },
  model: {
    prop: 'value',
    event: 'change'
  },
  data () {
    return {
      newVal: this.value,
      optionsPositions: {}
    }
  },
  methods: {
    change (value, index) {
      if (this.disabled) return
      let targetValue = value

      if (targetValue === this.newVal && this.options.length === 2) {
        const anotherIndex = ~index & 1
        targetValue = this.options[anotherIndex].value
      }

      this.checked = targetValue
      this.slideTo(targetValue)
      this.$emit('change', targetValue)
    },

    /**
     * 滑动滑块到指定 option
     *
     * @param {number | string} value 滑动目标 option 的 value
     */
    slideTo (value) {
      const optionPosition = this.optionsPositions[value]
      if (!optionPosition) {
        return
      }

      const slider = this.$refs.slider

      slider.style.transform = `translateX(${optionPosition.offset}px)`
      slider.style.width = `${optionPosition.width}px`
    },

    /**
     * 探测各 option 的位置
     */
    determineOptionsPositions () {
      const optionElements = this.$el.querySelectorAll('.content')

      const positionsArr = []
      let accOffset = 0
      optionElements.forEach(el => {
        const width = el.getBoundingClientRect().width || parseFloat(getComputedStyle(el).width)

        positionsArr.push({
          width: width,
          offset: accOffset
        })

        accOffset += width
      })

      const optionsPositions = {}
      this.options.forEach((option, i) => {
        optionsPositions[option.value] = positionsArr[i]
      })

      this.optionsPositions = optionsPositions
    }
  },
  watch: {
    value: function (val) {
      this.newVal = val
      setTimeout(() => this.slideTo(this.value), 0)
    },

    options () {
      this.$nextTick(this.determineOptionsPositions)
    }
  },

  mounted () {
    this.determineOptionsPositions()
    this.slideTo(this.value)
  }
}
</script>

<style scoped lang="stylus">
  .wrap {
    overflow hidden
    position relative
    border-radius 20px
    box-shadow 0 0 8px 0 rgba(0, 0, 0, 0.25) inset
  }

  .disabled {
    cursor not-allowed
  }

  .main {
    white-space nowrap
    float left
    border 1px solid #D3D3D3
    cursor pointer
    width 100%
  }

  .content {
    padding 0 10px
    height 24px
    box-sizing border-box
    line-height 24px
    flex 1
    list-style none
    font-size 14px
    font-weight 500
    color #303133
    position relative
    text-align center
  }

  .active {
    color #5F77FD
  }

  .slider {
    position absolute
    top 1px
    left 1px
    height 24px
    width 0
    background #FFF
    border-radius 20px
    box-shadow 0 0 7px 0 rgba(0, 0, 0, 0.15)
    transition all .3s ease
  }
</style>
